package org.bdware.repository.storage;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.reflect.TypeToken;
import org.bdware.doip.core.codec.DigitalObjectCodec;
import org.bdware.doip.core.model.digitalObject.DigitalObject;
import org.bdware.doip.implementation.DoStorageRocksDBImpl;
import org.bdware.repository.webService.dto.Stat;
import org.rocksdb.RocksDB;
import org.rocksdb.RocksDBException;

import java.util.HashMap;

public class BDRepoStorage extends DoStorageRocksDBImpl {

    static final byte[] USER_STATS = "UserStats".getBytes();
    static final String ANONYMOUS = "anonymous";
    String requester = ANONYMOUS;
    HashMap<String, JsonArray> UserStats;

    public BDRepoStorage(String path) throws RocksDBException {
        super((path));
        loadStats();
    }

    public BDRepoStorage() throws RocksDBException {
        this("./doStorage/");
    }

    void loadStats() throws RocksDBException {
        if (getDBInstance() == null) return;
        RocksDB db = getDBInstance();
        byte[] statsBytes = db.get(USER_STATS);
        if (statsBytes == null) {
            UserStats = new HashMap<>();
            UserStats.put(ANONYMOUS, new JsonArray());
        } else {
            UserStats = new Gson().fromJson(new String(statsBytes), new TypeToken<HashMap<String, JsonArray>>() {
            }.getType());
        }
    }

    void saveStats() throws RocksDBException {
        if (getDBInstance() == null) return;
        RocksDB db = getDBInstance();
        byte[] statsBytes = new Gson().toJson(UserStats).getBytes();
        db.put(USER_STATS, statsBytes);
    }

    @Override
    public void storeDo(DigitalObject digitalObject) {
        DigitalObjectCodec doCodec = getDoCodec();
        RocksDB db = getDBInstance();
        try {
            db.put(digitalObject.id.getBytes(), doCodec.DoToByteArray(digitalObject));
            addDOStats(requester, digitalObject.id);
            saveStats();
        } catch (RocksDBException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void deleteDoByID(String id) {
        RocksDB db = getDBInstance();
        try {
            db.delete(id.getBytes());
            deleteDOStats(requester, id);
            saveStats();
        } catch (RocksDBException e) {
            e.printStackTrace();
        }
    }

    /**
     * need to set requester before delete/add each time
     *
     * @param requester
     * @return
     */
    public BDRepoStorage setRequester(String requester) {
        if (requester != null) this.requester = requester;
        return this;
    }

    void addDOStats(String owner, String doID) {
        if (!UserStats.containsKey(owner)) {
            UserStats.put(owner, new JsonArray());
        }
        UserStats.get(owner).add(doID);
        requester = ANONYMOUS;      //reset the requester
    }

    void deleteDOStats(String owner, String doID) {
        if (!UserStats.containsKey(owner)) {
            return;
        }
        JsonArray ja = UserStats.get(owner);
        for (int i = 0; i < ja.size(); i++) {
            if (ja.get(i).getAsString().equals(doID)) {
                ja.remove(i);
                break;
            }
        }
        requester = ANONYMOUS;      //reset the requester
    }

    /**
     * get DO List by user ID
     *
     * @return DO statistics
     */
    public HashMap<String, JsonArray> getUserStats() {
        return UserStats;
    }

    public JsonArray getDOsByUserID(String userID) {
        return UserStats.get(userID);
    }

    public byte[] getRaw(byte[] id) throws RocksDBException {
        RocksDB db = getDBInstance();
        return db.get(id);
    }

    public void putRaw(byte[] id, byte[] content) throws RocksDBException {
        getDBInstance().put(id, content);
    }

    /**
     * get all repo stat
     * @return all repo stat
     */
    public Stat getStat() {
        try {
            loadStats();
        } catch (RocksDBException e) {
            e.printStackTrace();
        }
        Stat stat = new Stat();
        stat.setUserNum(getUserStats().size());

        return stat;
    }
}
